/*
 * Copyright 2016 drakeet. https://github.com/drakeet
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *    http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.foxmail.aroundme.mutlitype;

import android.support.annotation.IntDef;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.v7.widget.RecyclerView;
import android.support.v7.widget.RecyclerView.ViewHolder;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;

import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
import java.util.List;

/**
 * 这层Adapter封装数据存储，整个数据存错放在这里
 */
public class MultiTypeAdapter extends RecyclerView.Adapter<ViewHolder>
        implements FlatTypeAdapter, TypePool {
    @NonNull
    protected List<BaseItem> data;
    @NonNull
    private TypePool typePool;
    @Nullable
    private LayoutInflater inflater;
    @Nullable
    private FlatTypeAdapter providedFlatTypeAdapter;

    //Item点击
    private OnItemClickListener mClickListener;

    @IntDef({HEADER,FOOTER})
    @Retention(RetentionPolicy.SOURCE)
    private @interface HEADER_FOOTER_TYPE {
    }

    public static final int HEADER = 0;
    public static final int FOOTER = 1;


    public MultiTypeAdapter(@NonNull List<BaseItem> data) {
        this(data, new MultiTypePool(), null);
    }

    //限制Pool中的个数
    public MultiTypeAdapter(@NonNull List<BaseItem> data, int initialCapacity) {
        this(data, new MultiTypePool(initialCapacity), null);
    }


    public MultiTypeAdapter(@NonNull List<BaseItem> data, TypePool pool) {
        this(data, pool, null);
    }


    public MultiTypeAdapter(
            @NonNull List<BaseItem> data, @NonNull TypePool typePool,
            @Nullable FlatTypeAdapter providedFlatTypeAdapter) {
        this.data = data;
        this.typePool = typePool;
        this.providedFlatTypeAdapter = providedFlatTypeAdapter;
    }


    @Override
    public int getItemViewType(int position) {
        BaseItem item = data.get(position);
        return indexOf(flattenClass(item));
    }


    @Override
    public ViewHolder onCreateViewHolder(ViewGroup parent, int indexViewType) {
        if (inflater == null) {
            inflater = LayoutInflater.from(parent.getContext());
        }
        ItemViewProvider provider = getProviderByIndex(indexViewType);
        provider.adapter = MultiTypeAdapter.this;
        return provider.onCreateViewHolder(inflater, parent);
    }


    @Override
    public void onBindViewHolder(ViewHolder holder, int position) {
        BaseItem item = data.get(position);
        ItemViewProvider provider = getProviderByClass(flattenClass(item));
        provider.onBindViewHolder(holder, flattenItem(item));
        if (mClickListener != null) {
            mClickListener.onItemClick(holder.itemView, holder);
        }
    }

    public void setOnItemClickListener(OnItemClickListener listener) {
        mClickListener = listener;
    }

    @Override
    public int getItemCount() {
        return data.size();
    }


    @Override
    public void register(@NonNull Class<?> clazz, @NonNull ItemViewProvider provider) {
        typePool.register(clazz, provider);
    }

    public void registerIndex(@NonNull BaseItem item, @NonNull ItemViewProvider provider,
                              @HEADER_FOOTER_TYPE int type) {
        //添加footer和header
        switch (type) {
            case MultiTypeAdapter.FOOTER:
                data.add(data.size(), item);
                break;

            case MultiTypeAdapter.HEADER:
                data.add(0, item);
                break;
        }
        register(item.getClass(), provider);

    }


    public void registerAll(@NonNull final MultiTypePool pool) {
        for (int i = 0; i < pool.getContents().size(); i++) {
            typePool.register(pool.getContents().get(i), pool.getProviders().get(i));
        }
    }


    @Override
    public int indexOf(@NonNull Class<?> clazz)
            throws ProviderNotFoundException {
        int index = typePool.indexOf(clazz);
        if (index >= 0) {
            return index;
        }
        throw new ProviderNotFoundException(clazz);
    }


    /**
     * Set the FlatTypeAdapter to instead of the default inner FlatTypeAdapter of
     * MultiTypeAdapter.
     * <p>Note: You could use {@link FlatTypeClassAdapter} and {@link FlatTypeItemAdapter}
     * to create a special FlatTypeAdapter conveniently.</p>
     *
     * @param flatTypeAdapter the FlatTypeAdapter
     * @since v2.3.2
     */
    public void setFlatTypeAdapter(@NonNull FlatTypeAdapter flatTypeAdapter) {
        this.providedFlatTypeAdapter = flatTypeAdapter;
    }


    @NonNull
    @SuppressWarnings("deprecation")
    Class flattenClass(@NonNull final BaseItem item) {
        if (providedFlatTypeAdapter != null) {
            return providedFlatTypeAdapter.onFlattenClass(item);
        }
        return onFlattenClass(item);
    }


    @NonNull
    BaseItem flattenItem(@NonNull final BaseItem item) {
        if (providedFlatTypeAdapter != null) {
            return providedFlatTypeAdapter.onFlattenItem(item);
        }
        return onFlattenItem(item);
    }


    /**
     * @deprecated Use {@link MultiTypeAdapter#setFlatTypeAdapter(FlatTypeAdapter)} instead.
     * The method may be removed next time.
     */
    @NonNull
    @Override
    public Class onFlattenClass(@NonNull final BaseItem item) {
        return item.getClass();
    }


    /**
     * @deprecated Use {@link MultiTypeAdapter#setFlatTypeAdapter(FlatTypeAdapter)} instead.
     * The method may be removed next time.
     */
    @NonNull
    @Override
    public BaseItem onFlattenItem(@NonNull final BaseItem item) {
        return item;
    }


    @NonNull
    @Override
    public ArrayList<Class<?>> getContents() {
        return typePool.getContents();
    }


    @NonNull
    @Override
    public ArrayList<ItemViewProvider> getProviders() {
        return typePool.getProviders();
    }


    @NonNull
    @Override
    public ItemViewProvider getProviderByIndex(int index) {
        return typePool.getProviderByIndex(index);
    }


    @NonNull
    @Override
    public <T extends ItemViewProvider> T getProviderByClass(@NonNull Class<?> clazz) {
        return typePool.getProviderByClass(clazz);
    }
}
